home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / lib / gedit-2 / plugins / externaltools / library.py < prev    next >
Encoding:
Python Source  |  2009-04-14  |  14.0 KB  |  429 lines

  1. # -*- coding: utf-8 -*-
  2. #    Gedit External Tools plugin
  3. #    Copyright (C) 2006  Steve Fr√©cinaux <code@istique.net>
  4. #
  5. #    This program is free software; you can redistribute it and/or modify
  6. #    it under the terms of the GNU General Public License as published by
  7. #    the Free Software Foundation; either version 2 of the License, or
  8. #    (at your option) any later version.
  9. #
  10. #    This program is distributed in the hope that it will be useful,
  11. #    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. #    GNU General Public License for more details.
  14. #
  15. #    You should have received a copy of the GNU General Public License
  16. #    along with this program; if not, write to the Free Software
  17. #    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  18.  
  19. import os
  20. import re
  21. import locale
  22. import platform
  23.  
  24. class Singleton(object):
  25.     _instance = None
  26.  
  27.     def __new__(cls, *args, **kwargs):
  28.         if not cls._instance:
  29.             cls._instance = super(Singleton, cls).__new__(
  30.                              cls, *args, **kwargs)
  31.             cls._instance.__init_once__()
  32.  
  33.         return cls._instance
  34.  
  35. class ToolLibrary(Singleton):
  36.     def __init_once__(self):
  37.         self.locations = []
  38.     
  39.     def set_locations(self, datadir):
  40.         self.locations = []
  41.  
  42.         if platform.platform() != 'Windows':
  43.             for d in self.get_xdg_data_dirs():
  44.                 self.locations.append(os.path.join(d, 'gedit-2/plugins/tools'))
  45.  
  46.         self.locations.append(datadir)
  47.  
  48.         # self.locations[0] is where we save the custom scripts
  49.         if platform.platform() == 'Windows':
  50.             self.locations.insert(0, os.path.expanduser('~/gedit/tools'))
  51.         else:
  52.             self.locations.insert(0, os.path.expanduser('~/.gnome2/gedit/tools'))
  53.  
  54.         if not os.path.isdir(self.locations[0]):
  55.             os.makedirs(self.locations[0])
  56.             self.tree = ToolDirectory(self, '')
  57.             self.import_old_xml_store()
  58.         else:
  59.             self.tree = ToolDirectory(self, '')
  60.  
  61.     # cf. http://standards.freedesktop.org/basedir-spec/latest/
  62.     def get_xdg_data_dirs(self):
  63.         dirs = os.getenv('XDG_DATA_DIRS')
  64.         if dirs:
  65.             dirs = dirs.split(os.pathsep)
  66.         else:
  67.             dirs = ('/usr/local/share', '/usr/share')
  68.         return dirs
  69.  
  70.     # This function is meant to be ran only once, when the tools directory is
  71.     # created. It imports eventual tools that have been saved in the old XML
  72.     # storage file.
  73.     def import_old_xml_store(self):
  74.         import ElementTree as et
  75.         filename = os.path.expanduser('~/.gnome2/gedit/gedit-tools.xml')
  76.         if not os.path.isfile(filename):
  77.             return
  78.  
  79.         print "External tools: importing old tools into the new store..."
  80.  
  81.         xtree = et.parse(filename)
  82.         xroot = xtree.getroot()
  83.  
  84.         for xtool in xroot:
  85.             for i in self.tree.tools:
  86.                 if i.name == xtool.get('label'):
  87.                     tool = i
  88.                     break
  89.             else:
  90.                 tool = Tool(self.tree)
  91.                 tool.name = xtool.get('label')
  92.                 tool.autoset_filename()
  93.                 self.tree.tools.append(tool)
  94.             tool.comment = xtool.get('description')
  95.             tool.shortcut = xtool.get('accelerator')
  96.             tool.applicability = xtool.get('applicability')
  97.             tool.output = xtool.get('output')
  98.             tool.input = xtool.get('input')                    
  99.             tool.save_with_script(xtool.text)
  100.  
  101.     def get_full_path(self, path, mode='r', system = True, local = True):
  102.         assert (system or local)
  103.         if path is None:
  104.             return None
  105.         if mode == 'r':
  106.             if system and local:
  107.                 locations = self.locations
  108.             elif local and not system:
  109.                 locations = [self.locations[0]]
  110.             elif system and not local:
  111.                 locations = self.locations[1:]
  112.             else:
  113.                 raise ValueError("system and local can't be both set to False")
  114.  
  115.             for i in locations:
  116.                 p = os.path.join(i, path)
  117.                 if os.path.lexists(p):
  118.                     return p
  119.             return None
  120.         else:
  121.             path = os.path.join(self.locations[0], path)
  122.             dirname = os.path.dirname(path)
  123.             if not os.path.isdir(dirname):
  124.                 os.mkdir(dirname)
  125.             return path
  126.  
  127. class ToolDirectory(object):
  128.     def __init__(self, parent, dirname):
  129.         super(ToolDirectory, self).__init__()
  130.         self.subdirs = list()
  131.         self.tools = list()
  132.         if isinstance(parent, ToolDirectory):
  133.             self.parent = parent
  134.             self.library = parent.library
  135.         else:
  136.             self.parent = None
  137.             self.library = parent
  138.         self.dirname = dirname
  139.         self._load()
  140.  
  141.     def listdir(self):
  142.         elements = dict()
  143.         for l in self.library.locations:
  144.             d = os.path.join(l, self.dirname)
  145.             if not os.path.isdir(d):
  146.                 continue
  147.             for i in os.listdir(d):
  148.                 elements[i] = None
  149.         keys = elements.keys()
  150.         keys.sort()
  151.         return keys
  152.  
  153.     def _load(self):
  154.         for p in self.listdir():
  155.             path = os.path.join(self.dirname, p)
  156.             full_path = self.library.get_full_path(path)
  157.             if os.path.isdir(full_path):
  158.                 self.subdirs.append(ToolDirectory(self, p))
  159.             elif os.path.isfile(full_path) and os.access(full_path, os.X_OK):
  160.                 self.tools.append(Tool(self, p))
  161.  
  162.     def get_path(self):
  163.         if self.parent is None:
  164.             return self.dirname
  165.         else:
  166.             return os.path.join(self.parent.get_path(), self.dirname)
  167.     path = property(get_path)
  168.  
  169.     def get_name(self):
  170.         return os.path.basename(self.dirname)
  171.     name = property(get_name)
  172.  
  173.     def delete_tool(self, tool):
  174.         # Only remove it if it lays in $HOME
  175.         if tool in self.tools:
  176.             path = tool.get_path()
  177.             if path is not None:
  178.                 filename = os.path.join(self.library.locations[0], path)
  179.                 if os.path.isfile(filename):
  180.                     os.unlink(filename)
  181.             self.tools.remove(tool)
  182.             return True
  183.         else:
  184.             return False
  185.  
  186.     def revert_tool(self, tool):
  187.         # Only remove it if it lays in $HOME
  188.         filename = os.path.join(self.library.locations[0], tool.get_path())
  189.         if tool in self.tools and os.path.isfile(filename):
  190.             os.unlink(filename)
  191.             tool._load()
  192.             return True
  193.         else:
  194.             return False
  195.  
  196.  
  197. class Tool(object):
  198.     RE_KEY = re.compile('^([a-zA-Z_][a-zA-Z0-9_.\-]*)(\[([a-zA-Z_@]+)\])?$')
  199.  
  200.     def __init__(self, parent, filename = None):
  201.         super(Tool, self).__init__()
  202.         self.parent = parent
  203.         self.library = parent.library
  204.         self.filename = filename
  205.         self.changed = False
  206.         self._properties = dict()
  207.         self._load()
  208.  
  209.     def _load(self):
  210.         if self.filename is None:
  211.             return
  212.  
  213.         filename = self.library.get_full_path(self.get_path())
  214.         if filename is None:
  215.             return
  216.  
  217.         fp = file(filename, 'r', 1)
  218.         in_block = False
  219.         lang = locale.getlocale(locale.LC_MESSAGES)[0]
  220.  
  221.         for line in fp:
  222.             if not in_block:
  223.                 in_block = line.startswith('# [Gedit Tool]')
  224.                 continue
  225.             if line.startswith('##') or line.startswith('# #'): continue
  226.             if not line.startswith('# '): break
  227.  
  228.             try:
  229.                 (key, value) = [i.strip() for i in line[2:].split('=', 1)]
  230.                 m = self.RE_KEY.match(key)
  231.                 if m.group(3) is None:
  232.                     if m.group(0) not in self._properties:
  233.                         self._properties[m.group(1)] = value
  234.                 elif lang is not None and lang.startswith(m.group(3)):
  235.                     self._properties[m.group(1)] = value
  236.             except ValueError:
  237.                 break
  238.         fp.close()
  239.         self.changed = False
  240.  
  241.     def _set_property_if_changed(self, key, value):
  242.         if value != self._properties.get(key):
  243.             self._properties[key] = value
  244.             self.changed = True
  245.  
  246.     def is_global(self):
  247.         return self.library.get_full_path(self.get_path(), local=False) is not None
  248.  
  249.     def is_local(self):
  250.         return self.library.get_full_path(self.get_path(), system=False) is not None
  251.  
  252.     def is_global(self):
  253.         return self.library.get_full_path(self.get_path(), local=False) is not None
  254.  
  255.     def get_path(self):
  256.         if self.filename is not None:
  257.             return os.path.join(self.parent.get_path(), self.filename)
  258.         else:
  259.             return None
  260.     path = property(get_path)
  261.  
  262.     # This command is the one that is meant to be ran
  263.     # (later, could have an Exec key or something)
  264.     def get_command(self):
  265.         return self.library.get_full_path(self.get_path())
  266.     command = property(get_command)
  267.  
  268.     def get_applicability(self):
  269.         applicability = self._properties.get('Applicability')
  270.         if applicability: return applicability
  271.         return 'all'
  272.     def set_applicability(self, value):
  273.         self._set_property_if_changed('Applicability', value)
  274.     applicability = property(get_applicability, set_applicability)
  275.  
  276.     def get_name(self):
  277.         name = self._properties.get('Name')
  278.         if name: return name
  279.         return os.path.basename(self.filename)
  280.     def set_name(self, value):
  281.         self._set_property_if_changed('Name', value)
  282.     name = property(get_name, set_name)
  283.  
  284.     def get_shortcut(self):
  285.         shortcut = self._properties.get('Shortcut')
  286.         if shortcut: return shortcut
  287.         return None
  288.     def set_shortcut(self, value):
  289.         self._set_property_if_changed('Shortcut', value)
  290.     shortcut = property(get_shortcut, set_shortcut)
  291.  
  292.     def get_comment(self):
  293.         comment = self._properties.get('Comment')
  294.         if comment: return comment
  295.         return self.filename
  296.     def set_comment(self, value):
  297.         self._set_property_if_changed('Comment', value)
  298.     comment = property(get_comment, set_comment)
  299.  
  300.     def get_input(self):
  301.         input = self._properties.get('Input')
  302.         if input: return input
  303.         return 'nothing'
  304.     def set_input(self, value):
  305.         self._set_property_if_changed('Input', value)
  306.     input = property(get_input, set_input)
  307.  
  308.     def get_output(self):
  309.         output = self._properties.get('Output')
  310.         if output: return output
  311.         return 'output-panel'
  312.     def set_output(self, value):
  313.         self._set_property_if_changed('Output', value)
  314.     output = property(get_output, set_output)
  315.  
  316.     # There is no property for this one because this function is quite
  317.     # expensive to perform
  318.     def get_script(self):
  319.         if self.filename is None:
  320.             return []
  321.  
  322.         filename = self.library.get_full_path(self.get_path())
  323.         if filename is None:
  324.             return []
  325.  
  326.         fp = open(filename, 'r', 1)
  327.         lines = list()
  328.  
  329.         # before entering the data block
  330.         for line in fp:
  331.             if line.startswith('# [Gedit Tool]'):
  332.                 break
  333.             lines.append(line)
  334.         # in the block:
  335.         for line in fp:
  336.             if line.startswith('##'): continue
  337.             if not (line.startswith('# ') and '=' in line):
  338.                 # after the block: strip one emtpy line (if present)
  339.                 if line.strip() != '':
  340.                     lines.append(line)
  341.                 break
  342.         # after the block
  343.         for line in fp:
  344.             lines.append(line)
  345.         fp.close()
  346.         return lines
  347.  
  348.     def _dump_properties(self):
  349.         lines = ['# [Gedit Tool]']
  350.         for item in self._properties.iteritems():
  351.             if item[1] is not None:
  352.                 lines.append('# %s=%s' % item)
  353.         return '\n'.join(lines) + '\n'
  354.  
  355.     def save_with_script(self, script):
  356.         filename = self.library.get_full_path(self.filename, 'w')
  357.         fp = open(filename, 'w', 1)
  358.  
  359.         try:
  360.             script = iter(script)
  361.             line = script.next()
  362.  
  363.             # Shebang (should be always present)
  364.             if line.startswith('#!'):
  365.                 fp.write(line)
  366.                 line = script.next()
  367.             # Emacs modeline (might or might not be there)
  368.             if '-*-' in line:
  369.                 fp.write(line)
  370.                 line = script.next()
  371.             # Vim modeline
  372.             if 'ex:' in line or 'vi:' in line or 'vim:' in line:
  373.                 fp.write(line)
  374.                 line = script.next()
  375.             # We put a white line before the info block if there is one available
  376.             if line.strip() == '':
  377.                 fp.write(line)
  378.                 line = script.next()
  379.             # Write the info block
  380.             fp.write(self._dump_properties())
  381.             fp.write("\n")
  382.             # And write the remaining part of the script
  383.             while True:
  384.                 fp.write(line)
  385.                 line = script.next()
  386.         except StopIteration:
  387.             pass
  388.  
  389.         fp.close()
  390.         os.chmod(filename, 0750)
  391.         self.changed = False
  392.  
  393.     def save(self):
  394.         if self.changed:
  395.             self.save_with_script(self.get_script())
  396.  
  397.     def autoset_filename(self):
  398.         if self.filename is not None:
  399.             return
  400.         dirname = self.parent.path
  401.         if dirname != '':
  402.             dirname += os.path.sep
  403.  
  404.         basename = self.name.lower().replace(' ', '-').replace('/', '-')
  405.  
  406.         if self.library.get_full_path(dirname + basename):
  407.             i = 2
  408.             while self.library.get_full_path(dirname + "%s-%d" % (basename, i)):
  409.                 i += 1
  410.             basename = "%s-%d" % (basename, i)
  411.         self.filename = basename
  412.  
  413. if __name__ == '__main__':
  414.     library = ToolLibrary()
  415.  
  416.     def print_tool(t, indent):
  417.         print indent * "  " + "%s: %s" % (t.filename, t.name)
  418.  
  419.     def print_dir(d, indent):
  420.         print indent * "  " + d.dirname + '/'
  421.         for i in d.subdirs:
  422.             print_dir(i, indent+1)
  423.         for i in d.tools:
  424.             print_tool(i, indent+1)
  425.  
  426.     print_dir(library.tree, 0)
  427.  
  428. # ex:ts=4:et:
  429.